package com.ndk.opengl;

import android.app.Activity;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.opengl.EGL14;
import android.opengl.EGLContext;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.util.Log;

import com.ndk.opengl.face.FaceTrack;
import com.ndk.opengl.filter.BeautyFilter;
import com.ndk.opengl.filter.BigEyeFilter;
import com.ndk.opengl.filter.CameraFilter;
import com.ndk.opengl.filter.ScreenFilter;
import com.ndk.opengl.record.MyMediaRecorder;
import com.ndk.opengl.utils.FileUtil;

import java.io.IOException;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import androidx.annotation.RequiresApi;

import static android.opengl.GLES20.*;

/**
 * @author: sziitjin
 * @description: 自定义渲染器
 * @date: 2023/4/24
 */
public class MyGlRenderer implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener
        , Camera.PreviewCallback {
    private static final String TAG = "MyGlRenderer";

    private MyGLSurfaceView myGLSurfaceView;
    private CameraHelper mCameraHelper;
    private int[] mTextureID;
    private SurfaceTexture mSurfaceTexture; // 纹理对象
    private CameraFilter mCameraFilter; // 相机过滤器
    private ScreenFilter mScreenFilter; // 屏幕过滤器
    private float[] mtx = new float[16]; // 矩阵数据，变换矩阵
    private MyMediaRecorder mMediaRecorder;
    private int mWidth;
    private int mHeight;

    private BigEyeFilter mBigEyeFilter; // TODO 【大眼过滤器】
    private FaceTrack mFaceTrack; // 人脸 与 关键点 定位追踪
    private BeautyFilter mBeautyFilter; // TODO 【美颜过滤器】

    private String modelPath; // OpenCV的模型路径
    private String seetaPath; // 中科院的模型路径

    public MyGlRenderer(MyGLSurfaceView gLSurfaceView) {
        this.myGLSurfaceView = gLSurfaceView;
        // TODO 【大眼相关代码】  assets Copy到APP私有目录
        modelPath = FileUtil.copyAsset2Dir(myGLSurfaceView.getContext(), "lbpcascade_frontalface.xml"); // OpenCV的模型
        seetaPath = FileUtil.copyAsset2Dir(myGLSurfaceView.getContext(), "seeta_fa_v1.1.bin"); // 中科院的模型
        Log.i(TAG, "init modelPath:" + modelPath);
        Log.i(TAG, "init seetaPath:" + seetaPath);
    }

    /**
     * Surface创建时 回调此函数
     *
     * @param gl     OpenGL1.0 和 OpenGL2.0 差距很大，无法兼容下面了，遗留下来了之前的 1.0
     * @param config 配置项 目前用不到
     */
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        Log.i(TAG, "onSurfaceCreated");
        mCameraHelper = new CameraHelper((Activity) myGLSurfaceView.getContext(), // 上下文
                Camera.CameraInfo.CAMERA_FACING_FRONT, // 前置摄像头
                640, 480);
        mCameraHelper.setPreviewCallback(this);
        // 获取纹理ID【先理解成画布】
        mTextureID = new int[1];
        /**
         * 1.长度 只有一个 1
         * 2.纹理ID，是一个数组
         * 3.offset:0 使用数组的0下标
         */
        glGenTextures(mTextureID.length, mTextureID, 0);
        mSurfaceTexture = new SurfaceTexture(mTextureID[0]); // 实例化纹理对象
        mSurfaceTexture.setOnFrameAvailableListener(this); // 绑定好此监听 SurfaceTexture.OnFrameAvailableListener
        mCameraFilter = new CameraFilter(myGLSurfaceView.getContext()); // 先 FBO
        mScreenFilter = new ScreenFilter(myGLSurfaceView.getContext()); // 后 渲染屏幕

        // TODO 初始化录制工具类
        EGLContext eglContext = EGL14.eglGetCurrentContext();
        mMediaRecorder = new MyMediaRecorder(480, 800, FileUtil.getFilePath(), eglContext,
                myGLSurfaceView.getContext());
    }

    /**
     * Surface 改变时 回调此函数
     *
     * @param gl     OpenGL1.0 和 OpenGL2.0 差距很大，无法兼容下面了，遗留下来了之前的 1.0
     * @param width  宽
     * @param height 高
     */
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        Log.i(TAG, "onSurfaceChanged");
        mWidth = width;
        mHeight = height;

        // 创建人脸检测跟踪器 // TODO 【大眼相关代码】
        mFaceTrack = new FaceTrack(modelPath, seetaPath, mCameraHelper);
        mFaceTrack.startTrack(); // 启动跟踪器

        mCameraHelper.startPreview(mSurfaceTexture); // 开始预览
        mCameraFilter.onReady(width, height);
        mScreenFilter.onReady(width, height);
    }

    /**
     * 绘制一帧图像时 回调此函数
     *
     * @param gl OpenGL1.0 和 OpenGL2.0 差距很大，无法兼容下面了，遗留下来了之前的 1.0
     */
    @Override
    public void onDrawFrame(GL10 gl) {
        Log.i(TAG, "onDrawFrame");
        // 每次清空之前的：例子：上课擦黑白 是一个道理
        glClearColor(255, 0, 0, 0); // 屏幕清理成颜色 红色，清理成红色的黑板一样
        // mask 细节看看此文章：https://blog.csdn.net/z136411501/article/details/83273874
        // GL_COLOR_BUFFER_BIT 颜色缓冲区
        // GL_DEPTH_BUFFER_BIT 深度缓冲区
        // GL_STENCIL_BUFFER_BIT 模型缓冲区
        glClear(GL_COLOR_BUFFER_BIT);

        // 绘制摄像头数据
        mSurfaceTexture.updateTexImage();  // 将纹理图像更新为图像流中最新的帧数据【刷新一下】
        // 画布，矩阵数据，通过Native层将数据存储到mtx
        mSurfaceTexture.getTransformMatrix(mtx);
        // 相机过滤器，绘制一帧图像，不可见
        mCameraFilter.setMatrix(mtx);
        int textureId = mCameraFilter.onDrawFrame(mTextureID[0]); // 摄像头，矩阵，都已经做了
        // 增加其他特效
        /*textureId = 美白.onDrawFrame(textureId);
        textureId = 大眼.onDrawFrame(textureId);
        textureId = xxx.onDrawFrame(textureId);*/
        // TODO 【大眼相关代码】 textureId = 大眼Filter.onDrawFrame(textureId);
        if (null != mBigEyeFilter) {
            mBigEyeFilter.setFace(mFaceTrack.getFace());
            textureId = mBigEyeFilter.onDrawFrame(textureId);
        }

        // TODO 【美颜相关代码】
        if (null != mBeautyFilter) { // 没有不需要 人脸追踪/人脸关键点，整个屏幕美颜
            textureId = mBeautyFilter.onDrawFrame(textureId);
        }

        // 屏幕过滤器，绘制一帧图像，屏幕显示
        mScreenFilter.onDrawFrame(textureId); // textureId == 最终成果的纹理ID

        mMediaRecorder.encodeFrame(textureId, mSurfaceTexture.getTimestamp());
    }

    /**
     * 有可用的数据时，回调此函数，效率高，麻烦，需要手动调用一次才会渲染
     *
     * @param surfaceTexture
     */
    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        Log.i(TAG, "onFrameAvailable");
        myGLSurfaceView.requestRender(); // setRenderMode(RENDERMODE_WHEN_DIRTY); 配合用
    }

    public void surfaceDestroyed() {
        mCameraHelper.stopPreview();
        if (mFaceTrack != null)
            mFaceTrack.stopTrack(); // 停止跟踪器
    }

    /**
     * 圆形红色按钮的 按住拍 开始录制
     *
     * @param speed 录制的：极慢 慢 标准 快 极快
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    public void startRecording(float speed) {
        Log.e("MyGLRender", "startRecording speed:" + speed);
        try {
            mMediaRecorder.start(speed);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 圆形红色按钮的 松开拍 的 录制完成
     */
    public void stopRecording() {
        Log.e("MyGLRender", "stopRecording");
        mMediaRecorder.stop();
    }

    /**
     * // TODO 【大眼相关代码】
     * 开启大眼特效
     *
     * @param isChecked
     */
    public void enableBigEye(final boolean isChecked) {
        // BigEyeFilter bigEyeFilter = new BigEyeFilter(); // 这样可以吗  不行，必须在EGL线程里面绘制
        myGLSurfaceView.queueEvent(new Runnable() { // 把大眼渲染代码，加入到， GLSurfaceView 的 内置EGL 的 GLTHread里面
            public void run() {
                if (isChecked) {
                    mBigEyeFilter = new BigEyeFilter(myGLSurfaceView.getContext());
                    mBigEyeFilter.onReady(mWidth, mHeight);
                } else {
                    mBigEyeFilter.release();
                    mBigEyeFilter = null;
                }
            }
        });
    }

    /**
     * TODO 开启美颜
     *
     * @param isChecked checkbox复选框是否勾上了
     */
    public void enableBeauty(final boolean isChecked) {
        myGLSurfaceView.queueEvent(new Runnable() {
            public void run() {
                if (isChecked) {
                    mBeautyFilter = new BeautyFilter(myGLSurfaceView.getContext());
                    mBeautyFilter.onReady(mWidth, mHeight);
                } else {
                    mBeautyFilter.release();
                    mBeautyFilter = null;
                }
            }
        });
    }

    // Camera画面只有有数据，就会回调此函数
    @Override // 要把相机的数据，给C++层做人脸追踪  // TODO 【大眼相关代码】
    public void onPreviewFrame(byte[] data, Camera camera) {
        if (mFaceTrack != null)
            mFaceTrack.detector(data);
    }
}
